﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Engine;
using Microsoft.Xna.Framework;
using PlaytimeSaveTypes;
namespace Playtime
{
    public class JointAnimation
    {
        private JointStructure structure;
        public string Name { get; private set; }

        Dictionary<float, Dictionary<string,JointData>> keyframes;
        public float animationSpeed = 1f;

        float animationLength;
        float animationTimer = 0;
        
        public bool IsPlaying = false;

        public JointAnimation(JointStructure _structure, string _name)
        {
            structure = _structure;
            Name = _name;
        }

        public JointAnimation(JointAnimationSaveData _data, JointStructure _structure, string _name) : this(_structure, _name)
        {
            keyframes = new Dictionary<float, Dictionary<string, JointData>>();
            animationLength = _data.animationLength;
            animationSpeed = _data.animationSpeed;

            foreach (KeyValuePair<float, List<JointData>> keyframe in _data.keyframes)
            {
                AddKeyFrame(keyframe.Value, keyframe.Key);
            }
        }

        public void AddKeyFrame(List<JointData> _changedJoints, float _key)
        {
            Dictionary<string, JointData> newKeyframeData = new Dictionary<string, JointData>();
            foreach (Joint joint in structure.Joints.Values)
            {
                JointData newJointData = new JointData(joint.Name, joint.relativeRotation, joint.relativeTranslation);
                for (int i = 0; i < _changedJoints.Count; i++)
                {
                    if (joint.Name == _changedJoints[i].Name)
                    {
                        newJointData.relativeRotation = _changedJoints[i].relativeRotation;
                        newJointData.relativeTranslation = _changedJoints[i].relativeTranslation;
                        break;
                    }
                }
                newKeyframeData.Add(joint.Name, newJointData);
            }
            keyframes[_key] = newKeyframeData;
        }


        public void Update(GameTime _gameTime)
        {
            if (!IsPlaying)
                return;

            animationTimer += (float)_gameTime.ElapsedGameTime.TotalSeconds * animationSpeed;

            if (animationTimer > animationLength)
                animationTimer = 0;

            if (keyframes.Count == 0)
                return;

            List<float> keyValues = new List<float>(keyframes.Keys);
            keyValues.Sort();

            int min;
            int max;
            BinarySearchNearest(keyValues, animationTimer, out min, out max);

            min %= keyValues.Count;
            max %= keyValues.Count;
            Dictionary<string, JointData> frame1 = keyframes[keyValues[min]];
            Dictionary<string, JointData> frame2 = keyframes[keyValues[max]];
            if (min != max)
            {
                float percent = MathHelper.Clamp((animationTimer - keyValues[min]) / (keyValues[max] - keyValues[min]), 0, 1);

                foreach(KeyValuePair<string, Joint> joint in structure.Joints)
                {
                    joint.Value.relativeRotation = MathHelper.Lerp(frame1[joint.Key].relativeRotation, frame2[joint.Key].relativeRotation, percent);
                    joint.Value.relativeTranslation = Vector2.Lerp(frame1[joint.Key].relativeTranslation, frame2[joint.Key].relativeTranslation, percent);
                }
            }
            else
            {
                foreach (KeyValuePair<string, Joint> joint in structure.Joints)
                {
                    joint.Value.relativeRotation = frame1[joint.Key].relativeRotation;
                    joint.Value.relativeTranslation = frame1[joint.Key].relativeTranslation;
                }
            }
        }

        public static void BinarySearchNearest(List<float> _list, float _key, out int min, out int max)
        {
            min = 0;
            float smallestDist = float.PositiveInfinity;
            for (int i = 0; i < _list.Count; i++)
            {
                float dist = _key - _list[i];
                if (dist >= 0 && dist < smallestDist)
                {
                    smallestDist = dist;
                    min = i;
                }
            }

            max = 0;
            smallestDist = float.PositiveInfinity;
            for (int i = 0; i < _list.Count; i++)
            {
                float dist = _list[i] - _key;
                if (dist >= 0 && dist < smallestDist)
                {
                    smallestDist = dist;
                    max = i;
                }
            }
        }
    }
}
